home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 June: Reference Library / Dev.CD Jun 99 RL Disk 1.toast / What's New / Development Kits / Mac_OS_USB_DDK_v1.2 / Examples / USBModem / ModemDriver.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-04-15  |  33.5 KB  |  1,322 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        ModemDriver.c
  3.  
  4.     Contains:    Code for USB Modem driver
  5.  
  6.     Version:    xxx put version here xxx
  7.  
  8.     Copyright:    © 1998 by Apple Computer, Inc., all rights reserved.
  9.  
  10. */
  11. #include <DriverServices.h>
  12.  
  13. #include "Modem.h"
  14. #include "ModemDriver.h"
  15. #include "ShimSerialHAL.h"
  16.  
  17. static serPB     gSerialGlobals;
  18. static USBPB    syncPB, interruptPB, readPB, writePB, stallPB, delayPB;
  19.  
  20. /************************************************************************************/
  21. //
  22. //    immediateError
  23. //
  24. //    Determines whether it's an error or just pending.
  25. //
  26. /************************************************************************************/
  27.  
  28. static Boolean immediateError(OSStatus err)
  29. {
  30.     return ((err != kUSBPending) && (err != noErr));
  31. }
  32.  
  33. /************************************************************************************/
  34. //
  35. //    ConfigurationHandler
  36. //
  37. //    Configures the USB Serial Device (Modem).
  38. //
  39. /************************************************************************************/
  40.  
  41. static void ConfigurationHandler(USBPB *pb)
  42. {
  43. serPB                     *sp = &gSerialGlobals;
  44. USBEndPointDescriptor     *eDesc;
  45. OSStatus                err;
  46.  
  47.     TraceMessage(0, "\pEntering ConfigurationHandler");
  48.     
  49.     if(pb->usbStatus != noErr)
  50.     {
  51.         if((sp->onError == kReset) && (sp->retries > 0))
  52.         {
  53.             /* no idea what to do now?? */
  54.             USBExpertFatalError(sp->deviceRef, pb->usbStatus, sp->errorString, pb->usbRefcon);
  55.             
  56.             /* Mark port as errored */
  57.         }
  58.         else
  59.         {
  60.             StatusMessage(sp->deviceRef, sp->errorString, pb->usbStatus);
  61.             pb->usbRefcon = sp->onError;
  62.  
  63.             USBClearPipeStallByReference(pb->usbReference);
  64.             
  65.             /* we'll delay coming back to here */
  66.             sp->retries--;
  67.  
  68.             pb->usbReqCount = 0;
  69.             /* pb->usbFlags = kUSBtaskTime */
  70.             USBDelay(pb);
  71.  
  72.         }
  73.         
  74.         return;
  75.     }
  76.  
  77.     sp->onError = kReset;
  78.  
  79.     do{switch(pb->usbRefcon++)
  80.     {
  81.         case kStartConfig:
  82.             noteError("\pModem Driver driver does not recognize device (we'll try to config. it anyway)");            
  83.             if(sp->deviceDescriptor->numConf != 2)
  84.             {
  85.                 StatusMessage(sp->deviceRef, sp->errorString, 0);
  86.                 // break;    /* Now what ?? */
  87.             }
  88.             
  89.             /* Configure device to single configuration */
  90.             pb->usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBNone, kUSBStandard, kUSBDevice);            
  91.             
  92.             pb->usb.cntl.BRequest = kUSBRqSetConfig;
  93.             pb->usb.cntl.WValue = 2; /* select config #2 - seems config 1 is Win95 specific */
  94.             pb->usb.cntl.WIndex = 0;
  95.             pb->usbReqCount = 0;
  96.             pb->usbBuffer = nil;
  97.             sp->onError = 1;
  98.  
  99.             noteError("\pModem driver Setting Config");            
  100.             if(immediateError(err = USBDeviceRequest(pb)))
  101.             {
  102.                 USBExpertFatalError(sp->deviceRef, err, sp->errorString, 0);
  103.             }        
  104.         break;
  105.  
  106.         case kGetConfig:
  107.             /* get the full config descriptor */
  108.             pb->pbVersion = kUSBCurrentPBVersion;
  109.             pb->usb.cntl.WValue = 1;
  110.             if (sp->retries > 0)
  111.             {
  112.                 sp->onError = kGetConfig;
  113.             } else {
  114.                 sp->onError = kReset;
  115.             }
  116.             noteError("\pModem driver getting full config descriptor");            
  117.             if(immediateError(err = USBGetFullConfigurationDescriptor(pb)))
  118.             {
  119.                 USBExpertFatalError(sp->deviceRef, err, sp->errorString, 0);
  120.             }
  121.         
  122.         break;
  123.  
  124.         case kGetDataInterface:
  125.             /* remember the full config descriptor */
  126.             sp->conf = pb->usbBuffer;
  127.             StatusMessage(sp->deviceRef, "\pConfiguration Descriptor - ", (unsigned long)sp->conf);
  128.             
  129.             /* find the interface */
  130.             pb->usbReqCount = 0;
  131.             pb->usbClassType = kUSBDataClass;        /* data class */
  132.             pb->usbSubclass = 0;        /* any subclass */
  133.             pb->usbProtocol = 0;        /* any protocol */
  134.             pb->usb.cntl.WValue = 0;        /* alt */
  135.             if (sp->retries > 0)
  136.             {
  137.                 sp->onError = kGetDataInterface;
  138.             } else {
  139.                 sp->onError = kReset;
  140.             }
  141.             
  142.             noteError("\pModem driver parsing the Data interface");            
  143.             if(immediateError(err = USBFindNextInterfaceDescriptorImmediate(pb)))
  144.             {    /* this should return immediately */
  145.                 USBExpertFatalError(sp->deviceRef, err, sp->errorString, 0);
  146.             }
  147.             
  148.             /* remember the current position */
  149.             sp->interface = pb->usbBuffer;
  150.             StatusMessage(sp->deviceRef, "\pData Interface Descriptor - ", (unsigned long)sp->interface);
  151.             
  152.             sp->interfaceOffset = pb->usbReqCount;
  153.             
  154.             /* Find the out endpoint */
  155.             pb->usbFlags = kUSBOut;
  156.             pb->usbClassType = kUSBBulk;
  157.             pb->usbSubclass = 0;            /* Find the first one */
  158.             
  159.             noteError("\pModem driver parsing the bulk out endpoint");            
  160.             if(immediateError(err = USBFindNextEndpointDescriptorImmediate(pb)))
  161.             {    /* this should return immediately */
  162.                 USBExpertFatalError(sp->deviceRef, err, sp->errorString, 0);
  163.             }
  164.             
  165.  
  166.             sp->deviceRef = pb->usbReference;
  167.             
  168.             pb->usbFlags = kUSBOut;
  169.             /* The params are set up correctly so open it */
  170.             //eDesc = pb->usbBuffer;
  171.             //pb->usbValue = USBToHostWord(eDesc->maxPacketSize);
  172.             // max packet size is now returned in usbValue
  173.             
  174.             noteError("\pModem driver opening bulk out pipe");            
  175.             if(immediateError(err = USBOpenPipe(pb)))
  176.             {
  177.                 USBExpertFatalError(sp->deviceRef, err, sp->errorString, 0);
  178.             }        
  179.             
  180.             
  181.         break;
  182.     
  183.         case kGetEndpoint:
  184.             /* Out Endpoint is open */
  185.  
  186.             /* remember the ref */
  187.             sp->bulkOut = pb->usbReference;
  188.             StatusMessage(sp->deviceRef, "\pBulk Out Endpoint - ", sp->bulkOut);
  189.             
  190.             /* Find the in endpoint */
  191.  
  192.             /* restore the device ref, it gets overwritten by the open */
  193.             pb->usbReference = sp->deviceRef;
  194.             
  195.             /* set search back to just found interface */
  196.             pb->usbBuffer = sp->interface;
  197.             pb->usbReqCount = sp->interfaceOffset;
  198.             
  199.             pb->usbFlags = kUSBIn;
  200.             pb->usbClassType = kUSBBulk;
  201.             pb->usbSubclass = 0;            /* Find the first one */
  202.             if (sp->retries > 0)
  203.             {
  204.                 sp->onError = kGetEndpoint;
  205.             } else {
  206.                 sp->onError = kReset;
  207.             }
  208.             
  209.             noteError("\pModem driver parsing the bulk in endpoint");            
  210.             if(immediateError(err = USBFindNextEndpointDescriptorImmediate(pb)))
  211.             {    /* this should return immediatly */
  212.                 USBExpertFatalError(sp->deviceRef, err, sp->errorString, 0);
  213.             }
  214.             
  215.             pb->usbFlags = kUSBIn;
  216.             /* The params are set up correctly so open it */
  217.             eDesc = pb->usbBuffer;
  218.             pb->usbProtocol = USBToHostWord(eDesc->maxPacketSize);
  219.  
  220.             noteError("\pModem driver opening bulk in pipe");            
  221.             if(immediateError(err = USBOpenPipe(pb)))
  222.             {
  223.                 USBExpertFatalError(sp->deviceRef, err, sp->errorString, 0);
  224.             }        
  225.             
  226.             
  227.         break;
  228.     
  229.         case kGetCommInterface:
  230.             /* In Endpoint is open */
  231.  
  232.             /* remember the ref */
  233.             sp->bulkIn = pb->usbReference;
  234.             StatusMessage(sp->deviceRef, "\pBulk In Endpoint - ", sp->bulkIn);
  235.             
  236.             /* find the Comm Class interface */
  237.             pb->usbBuffer = sp->conf;            /* set it back to config desc. */
  238.             pb->usbReqCount = 0;
  239.             pb->usbFlags = 0;
  240.             pb->usbClassType = kUSBCommClass;        /* Comm class */
  241.             pb->usbSubclass = 2;        /* Abstract Control Model */
  242.             pb->usbProtocol = 1;        /* Hayes protocol */
  243.             pb->usb.cntl.WValue = 0;        /* alt */
  244.             if (sp->retries > 0)
  245.             {
  246.                 sp->onError = kGetCommInterface;
  247.             } else {
  248.                 sp->onError = kReset;
  249.             }
  250.             
  251.             noteError("\pModem driver parsing the Class interface");            
  252.             if(immediateError(err = USBFindNextInterfaceDescriptorImmediate(pb)))
  253.             {    /* this should return immediately */
  254.                 USBExpertFatalError(sp->deviceRef, err, sp->errorString, 0);
  255.             }
  256.             
  257.             /* remember the current position */
  258.             sp->clinterface = pb->usbBuffer;
  259.             StatusMessage(sp->deviceRef, "\pClass Interface Descriptor - ", (unsigned long)sp->clinterface);
  260.             
  261.             sp->clinterfaceOffset = pb->usbReqCount;
  262.             
  263.             /* Find the interrupt endpoint */
  264.  
  265.             /* restore the device ref, it gets overwritten by the open */
  266.             pb->usbReference = sp->deviceRef;
  267.             
  268.             /* set search back to just found interface */
  269.             pb->usbBuffer = sp->clinterface;
  270.             pb->usbReqCount = sp->clinterfaceOffset;
  271.             
  272.             pb->usbFlags = kUSBIn;
  273.             pb->usbClassType = kUSBInterrupt;
  274.             pb->usbSubclass = 0;            /* Find the first one */
  275.             if (sp->retries > 0)
  276.             {
  277.                 sp->onError = kGetCommInterface;
  278.             } else {
  279.                 sp->onError = kReset;
  280.             }
  281.             
  282.             noteError("\pModem Driver parsing the interrupt in endpoint");            
  283.             if(immediateError(err = USBFindNextEndpointDescriptorImmediate(pb)))
  284.             {    /* this should return immediatly */
  285.                 USBExpertFatalError(sp->deviceRef, err, sp->errorString, 0);
  286.             }
  287.             
  288.             pb->usbFlags = kUSBIn;
  289.             /* The params are set up correctly so open it */
  290.             eDesc = pb->usbBuffer;
  291.             pb->usbProtocol = USBToHostWord(eDesc->maxPacketSize);
  292.  
  293.             noteError("\pModem Driver opening interrupt in pipe");            
  294.             if(immediateError(err = USBOpenPipe(pb)))
  295.             {
  296.                 USBExpertFatalError(sp->deviceRef, err, sp->errorString, 0);
  297.             }        
  298.                         
  299.         break;
  300.  
  301.         case kConfigDone:
  302.             /* Interrupt is open */
  303.  
  304.             /* remember the ref */
  305.             sp->interrupt = pb->usbReference;
  306.             StatusMessage(sp->deviceRef, "\pInterrupt In Endpoint - ", sp->interrupt);
  307.             
  308.             StartStatusMonitor(sp->interrupt);
  309.             
  310.             // device is fully configured, ready for somebody to set the baud rate, etc.
  311.             // and start reading/writing to the bulk endpoints
  312.             syncPB.usbStatus = kAvailableStatus;
  313.             StatusMessage(sp->deviceRef, "\pModem configuration complete.", 0);
  314.         break;
  315.     
  316.         default:
  317.             noteError("\pInternal Error unused case in Modem Configuration handler");            
  318.             StatusMessage(sp->deviceRef, sp->errorString, (pb->usbRefcon-1));
  319.         break;
  320.     }
  321.     break;    /* only execute once, unless continue used */
  322.     }while(1);    /* so case can be reentered with a continue */
  323. }
  324.  
  325. UInt16    statusData[8];
  326.  
  327. /************************************************************************************/
  328. //
  329. //    ResetInterruptPB
  330. //
  331. //    Re-initialize the interrupt PB.
  332. //
  333. /************************************************************************************/
  334.  
  335. static void ResetInterruptPB(USBPB *pb)
  336. {
  337.     pb->usbReqCount = 16;
  338.     pb->usbBuffer = &statusData;
  339.     pb->usbStatus = noErr;
  340. }
  341.  
  342. UInt8     Intrretries = 0;
  343.  
  344. /************************************************************************************/
  345. //
  346. //    interruptCompletion
  347. //
  348. //    Interrupt completion handler.
  349. //
  350. /************************************************************************************/
  351.  
  352. static void interruptCompletion(USBPB *pb)
  353. {
  354.     serPB         *sp = &gSerialGlobals;
  355.     OSStatus    err;
  356.     unsigned char        newErrs = 0;
  357.     ShimSerialGlobals    *globals = gGlobals;
  358.     
  359.     TraceMessage(0, "\pEntering interruptCompletion");
  360.     
  361.     if (pb->usbStatus != kUSBAbortedError)    // are we being asked to quit?
  362.     {
  363.         if (pb->usbStatus != noErr)
  364.         {
  365.             StatusMessage(sp->deviceRef, "\pModemDriver: interruptCompletion error ", pb->usbStatus);
  366.             if (pb->usbStatus == kUSBEndpointStallErr)
  367.             {
  368.                 ClearDevice();
  369.             }
  370.             USBClearPipeStallByReference(gSerialGlobals.interrupt);
  371.             
  372.             Intrretries++;
  373.             if (Intrretries > 10)
  374.                 return;                    // we'll just give up for now
  375.                 
  376.             DoDelay();
  377.  
  378.         } else {
  379.             Intrretries = 0;
  380.             TraceMessage(0, "\pInterrupt received");
  381.             if ((pb->usbActCount > 2) && ((statusData[0] & 0x00FF) == kSerialState))
  382.             {
  383.                 sp->modemUSBStatus = HostToUSBWord(statusData[4]);
  384.                 
  385.                 if (sp->modemUSBStatus & kUSBParityErr) 
  386.                     newErrs |= parityErr;
  387.                 if (sp->modemUSBStatus & kUSBFramingErr) 
  388.                     newErrs |= framingErr;
  389.                 if (sp->modemUSBStatus & kUSBHwOverRunErr) 
  390.                     newErrs |= hwOverrunErr;
  391.                     
  392.                 globals->serStat.cumErrs |= newErrs;
  393.                 StatusMessage(sp->deviceRef, "\pModem Serial state - ", sp->modemUSBStatus);
  394.             }
  395.     }
  396.         
  397.     ResetInterruptPB(pb);
  398.  
  399.     if(immediateError(err = USBIntRead(pb)))
  400.     {
  401.             StatusMessage(sp->deviceRef, "\pCouldn't queue interrupt read!", err);
  402.         }
  403.     }    
  404. }
  405.  
  406. /************************************************************************************/
  407. //
  408. //    StartStatusMonitor
  409. //
  410. //    Kick off the interrupt mechanism.
  411. //
  412. /************************************************************************************/
  413.  
  414. static void StartStatusMonitor(USBPipeRef interruptPipe)
  415. {
  416.     USBPB        *pb = &interruptPB;
  417.     serPB         *sp = &gSerialGlobals;
  418.     OSStatus    err;
  419.     
  420.     TraceMessage(0, "\pEntering StartStatusMonitor");
  421.     
  422.     InitializePB(pb, interruptPipe, interruptCompletion);
  423.  
  424.     ResetInterruptPB(pb);
  425.  
  426.     if(immediateError(err = USBIntRead(pb)))
  427.     {
  428.         StatusMessage(sp->deviceRef, "\pCouldn't start interrupt read!", err);
  429.     }        
  430.  
  431. }
  432.  
  433. /************************************************************************************/
  434. //
  435. //    syncCompletion
  436. //
  437. //    Completion handler for all sync (setup) requests.
  438. //
  439. /************************************************************************************/
  440.  
  441. static void syncCompletion(USBPB *pb)
  442. {
  443.     serPB     *sp = &gSerialGlobals;
  444.  
  445.     TraceMessage(0, "\pEntering syncCompletion");
  446.  
  447.     if (pb->usbStatus != noErr)
  448.     {
  449.         USBExpertFatalError(sp->deviceRef, pb->usbStatus, "\pModemDriver: syncCompletion err for bRequest", pb->usb.cntl.BRequest);
  450.         if (pb->usbStatus == kUSBEndpointStallErr)
  451.         {
  452. //            ClearDevice();
  453.         }
  454.         USBClearPipeStallByReference(pb->usbReference);
  455.     }
  456.         
  457.     pb->usbStatus = kAvailableStatus;
  458. }
  459.  
  460. /************************************************************************************/
  461. //
  462. //    TimeoutPrevRequest
  463. //
  464. //    Timeout for all sync. requests.
  465. //
  466. /************************************************************************************/
  467.  
  468. Boolean TimeoutPrevRequest(void)
  469. {
  470.     USBPB        *pb = &syncPB;
  471.     serPB         *sp = &gSerialGlobals;
  472.     AbsoluteTime    startTime;
  473.     Duration        elapsedTime;
  474.  
  475.     if (pb->usbStatus == kAvailableStatus)
  476.         return false;
  477.     
  478.     startTime = UpTime();
  479.  
  480.     while (pb->usbStatus != kAvailableStatus){
  481.         elapsedTime = AbsoluteDeltaToDuration(UpTime(), startTime);
  482.         if (elapsedTime < 0) elapsedTime = elapsedTime/(-1000);      // make sure it's in milliseconds
  483.         if (elapsedTime > 100*durationMillisecond){
  484.             StatusMessage(sp->deviceRef, "\pModem driver, device request timeout - aborting", 0);
  485.             USBAbortPipeByReference(pb->usbReference);
  486.             return true;
  487.         }
  488.     }
  489.     return false;
  490. }
  491.  
  492. /************************************************************************************/
  493. //
  494. //    TimeoutStallRequest
  495. //
  496. //    Timeout for all stall clear requests.
  497. //
  498. /************************************************************************************/
  499.  
  500. Boolean TimeoutStallRequest(void)
  501. {
  502.     USBPB            *pb = &stallPB;
  503.     serPB             *sp = &gSerialGlobals;
  504.     AbsoluteTime    startTime;
  505.     Duration        elapsedTime;
  506.  
  507.     if (pb->usbStatus == kAvailableStatus)
  508.         return false;
  509.     
  510.     startTime = UpTime();
  511.  
  512.     while (pb->usbStatus != kAvailableStatus){
  513.         elapsedTime = AbsoluteDeltaToDuration(UpTime(), startTime);
  514.         if (elapsedTime < 0) elapsedTime = elapsedTime/(-1000);      // make sure it's in milliseconds
  515.         if (elapsedTime > 100*durationMillisecond){
  516.             StatusMessage(sp->deviceRef, "\pModem driver, Stall timeout - aborting", 0);
  517.             USBAbortPipeByReference(pb->usbReference);
  518.             return true;
  519.         }
  520.     }
  521.     return false;
  522. }
  523.  
  524. /************************************************************************************/
  525. //
  526. //    TimeoutDelayRequest
  527. //
  528. //    Timeout for delay requests.
  529. //
  530. /************************************************************************************/
  531.  
  532. Boolean TimeoutDelayRequest(void)
  533. {
  534.     USBPB            *pb = &delayPB;
  535.     serPB             *sp = &gSerialGlobals;
  536.     AbsoluteTime    startTime;
  537.     Duration        elapsedTime;
  538.  
  539.     if (pb->usbStatus == kAvailableStatus)
  540.         return false;
  541.     
  542.     startTime = UpTime();
  543.  
  544.     while (pb->usbStatus != kAvailableStatus){
  545.         elapsedTime = AbsoluteDeltaToDuration(UpTime(), startTime);
  546.         if (elapsedTime < 0) elapsedTime = elapsedTime/(-1000);      // make sure it's in milliseconds
  547.         if (elapsedTime > 100*durationMillisecond){
  548.             return true;
  549.         }
  550.     }
  551.     return false;
  552. }
  553.  
  554. /************************************************************************************/
  555. //
  556. //    USBSetBaudRate
  557. //
  558. //    Set up and send SetLineCoding request for baud rate only.
  559. //
  560. /************************************************************************************/
  561.  
  562. void USBSetBaudRate(UInt32 baudRate)
  563. {
  564.     USBPB        *pb = &syncPB;
  565.     serPB         *sp = &gSerialGlobals;
  566.     OSStatus     err;
  567.     UInt16        temp = 0;
  568.     UInt16        rateh;
  569.     Boolean        setdte = false;
  570.     
  571.     TraceMessage(0, "\pEntering USBSetBaudRate");
  572.     
  573.     if (TimeoutPrevRequest())
  574.         return;
  575.     
  576.     if (baudRate == kMaxBaudRate)        // We're going to lock to max for the moment
  577.     {
  578.     
  579.         // convert baudrate            I hate Intel...
  580.         temp = baudRate % 0x10000;
  581.         rateh = USBToHostWord(temp);
  582.         if (rateh != sp->Line_Settings.DTERate1)
  583.         {
  584.             sp->Line_Settings.DTERate1 = rateh;
  585.             setdte = true;
  586.         }
  587.         temp = baudRate / 0x10000;
  588.         rateh = USBToHostWord(temp);
  589.         if (rateh != sp->Line_Settings.DTERate2)
  590.         {
  591.             sp->Line_Settings.DTERate2 = rateh;
  592.             setdte = true;
  593.         }
  594.     
  595.         // Only do it if it's changing
  596.         if (setdte)
  597.         {        
  598.             pb->usbStatus = noErr;
  599.             pb->pbVersion = kUSBCurrentPBVersion;
  600.             pb->usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBOut, kUSBClass, kUSBInterface);    
  601.             pb->usb.cntl.BRequest = kSetLineCoding;
  602.             pb->usb.cntl.WValue = 0;
  603.             pb->usb.cntl.WIndex = 1; 
  604.             pb->usbReqCount = sizeof(sp->Line_Settings)-1;
  605.             pb->usbBuffer = &sp->Line_Settings;
  606.  
  607.             if(immediateError(err = USBDeviceRequest(pb)))
  608.             {
  609.                 StatusMessage(sp->deviceRef, "\pModem driver Setting baud rate", err);
  610.                 pb->usbStatus = kAvailableStatus;
  611.             }
  612.         }
  613.     }
  614. }
  615.  
  616. /************************************************************************************/
  617. //
  618. //    USBSetLineCoding
  619. //
  620. //    Set up and send SetLineCoding Request for all settings.
  621. //
  622. /************************************************************************************/
  623.  
  624. void USBSetLineCoding(LineParms Line_Coding)
  625. {
  626.     USBPB        *pb = &syncPB;
  627.     serPB         *sp = &gSerialGlobals;
  628.     OSStatus     err;
  629.     Boolean        setcoding = false;
  630.     
  631.     TraceMessage(0, "\pEntering USBSetLineCoding");
  632.     
  633.     if (TimeoutPrevRequest())
  634.         return;
  635.         
  636.     // save the settings only if they've changed
  637.     if (Line_Coding.CharFormat != sp->Line_Settings.CharFormat)
  638.     {
  639.         sp->Line_Settings.CharFormat = Line_Coding.CharFormat;
  640.         setcoding = true;
  641.     }
  642.     if (Line_Coding.ParityType != sp->Line_Settings.ParityType)
  643.     {
  644.         sp->Line_Settings.ParityType = Line_Coding.ParityType;
  645.         setcoding = true;
  646.     }
  647.     if (Line_Coding.DataBits != sp->Line_Settings.DataBits)
  648.     {
  649.         sp->Line_Settings.DataBits = Line_Coding.DataBits;
  650.         setcoding = true;
  651.     }
  652.     
  653.     // Don't do this if they haven't changed
  654.     if (setcoding)
  655.     {
  656.         pb->usbStatus = noErr;
  657.         pb->pbVersion = kUSBCurrentPBVersion;
  658.         pb->usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBOut, kUSBClass, kUSBInterface);                
  659.         pb->usb.cntl.BRequest = kSetLineCoding;
  660.         pb->usb.cntl.WValue = 0;
  661.         pb->usb.cntl.WIndex = 1; 
  662.         pb->usbReqCount = sizeof(sp->Line_Settings)-1;
  663.         pb->usbBuffer = &sp->Line_Settings;
  664.  
  665.         if(immediateError(err = USBDeviceRequest(pb)))
  666.         {
  667.             StatusMessage(sp->deviceRef, "\pModem driver Setting Line Coding", err);
  668.             pb->usbStatus = kAvailableStatus;
  669.         }
  670.     }
  671. }
  672.  
  673. /************************************************************************************/
  674. //
  675. //    USBSetControlLineState
  676. //
  677. //    Set up and send SetControlLineState (DTR and RTS).
  678. //
  679. /************************************************************************************/
  680.  
  681. void USBSetControlLineState(void)
  682. {
  683.     USBPB        *pb = &syncPB;
  684.     serPB         *sp = &gSerialGlobals;
  685.     OSStatus     err;
  686.     
  687.     TraceMessage(0, "\pEntering USBSetControlLineState");
  688.     
  689.     if (TimeoutPrevRequest())
  690.         return;
  691.  
  692.     pb->usbStatus = noErr;
  693.     pb->pbVersion = kUSBCurrentPBVersion;
  694.     pb->usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBOut, kUSBClass, kUSBInterface);
  695.     pb->usb.cntl.BRequest = kSetControlLineState;
  696.     pb->usb.cntl.WValue = USBToHostWord(sp->lineState);
  697.     pb->usb.cntl.WIndex = 1; 
  698.     pb->usbReqCount = 0;
  699.     pb->usbBuffer = 0;
  700.  
  701.     if(immediateError(err = USBDeviceRequest(pb)))
  702.     {
  703.         StatusMessage(sp->deviceRef, "\pModem driver Setting Control Line State", err);
  704.         pb->usbStatus = kAvailableStatus;
  705.     }
  706. }
  707.  
  708. /************************************************************************************/
  709. //
  710. //    USBSetDTRState
  711. //
  712. //    Set the DTR state.
  713. //
  714. /************************************************************************************/
  715.  
  716. void USBSetDTRState(Boolean state)
  717. {
  718.     serPB     *sp = &gSerialGlobals;
  719.  
  720.     TraceMessage(0, "\pEntering USBSetDTRState");
  721.  
  722.     if (state)
  723.     {
  724.         sp->lineState |= kDTROn;
  725.     } else {
  726.         if (!sp->DTRClose)
  727.             sp->lineState &= (kDTROff + kRTSOn);
  728.     }
  729. }
  730.  
  731. /************************************************************************************/
  732. //
  733. //    USBSetRTSState
  734. //
  735. //    Set the RTS state.
  736. //
  737. /************************************************************************************/
  738.  
  739. void USBSetRTSState(Boolean state)
  740. {
  741.     serPB     *sp = &gSerialGlobals;
  742.  
  743.     TraceMessage(0, "\pEntering USBSetRTSState");
  744.  
  745.     if (state)
  746.     {
  747.         sp->lineState |= kRTSOn;
  748.     } else {
  749.         sp->lineState &= (kRTSOff + kDTROn);
  750.     }
  751. }
  752.  
  753. /************************************************************************************/
  754. //
  755. //    USBSendBreak
  756. //
  757. //    Set up and send break request.
  758. //
  759. /************************************************************************************/
  760.  
  761. void USBSendBreak(Boolean state)
  762. {
  763.     USBPB        *pb = &syncPB;
  764.     serPB         *sp = &gSerialGlobals;
  765.     OSStatus     err;
  766.     
  767.     TraceMessage(0, "\pEntering USBSendBreak");
  768.     
  769.     if (TimeoutPrevRequest())
  770.         return;
  771.  
  772.     pb->usbStatus = noErr;
  773.     pb->pbVersion = kUSBCurrentPBVersion;
  774.     pb->usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBOut, kUSBClass, kUSBInterface);
  775.     pb->usb.cntl.BRequest = kSendBreak;
  776.     if (state)
  777.     {
  778.         pb->usb.cntl.WValue = kBreakOn;
  779.     } 
  780.     else 
  781.     {
  782.         pb->usb.cntl.WValue = kBreakOff;
  783.     }
  784.     pb->usb.cntl.WIndex = 1; 
  785.     pb->usbReqCount = 0;
  786.     pb->usbBuffer = 0;
  787.  
  788.     if(immediateError(err = USBDeviceRequest(pb)))
  789.     {
  790.         StatusMessage(sp->deviceRef, "\pModem driver Setting Break State", err);
  791.         pb->usbStatus = kAvailableStatus;
  792.     }
  793. }
  794.  
  795. /************************************************************************************/
  796. //
  797. //    USBSetCloseDTR
  798. //
  799. //    Set the state of DTR on close.
  800. //
  801. /************************************************************************************/
  802.  
  803. void USBSetCloseDTR(void)
  804. {
  805.     serPB     *sp = &gSerialGlobals;
  806.  
  807.     TraceMessage(0, "\pEntering USBSetCloseDTR");
  808.  
  809.     sp->DTRClose = true;
  810. }
  811.  
  812. /************************************************************************************/
  813. //
  814. //    USBGetDCDValue
  815. //
  816. //    Gets the current state of CD.
  817. //
  818. /************************************************************************************/
  819.  
  820. UInt8 USBGetDCDValue(void)
  821. {
  822.     serPB     *sp = &gSerialGlobals;
  823.     UInt8    dcd = 0;
  824.  
  825.     TraceMessage(0, "\pEntering USBGetDCDValue");
  826.  
  827.     if (sp->modemUSBStatus & kUSBDCD) 
  828.         dcd = 1;
  829.         
  830.     return dcd;
  831. }
  832.  
  833. /************************************************************************************/
  834. //
  835. //    ModemDriverEntry
  836. //
  837. //    Initializes and starts the whole show.
  838. //
  839. /************************************************************************************/
  840.  
  841. void modemDriverEntry(USBDeviceRef device, USBDeviceDescriptor *desc)
  842. {
  843. static Boolean                 beenThereDoneThat = false;
  844. static USBDeviceDescriptor     ourDeviceDescriptor;
  845.  
  846.     DebugMessage("\pEntering modemDriverEntry - Sync. point");
  847.         
  848.     if(beenThereDoneThat)
  849.     {
  850.         StatusMessage(device, "\pModem driver called second time", 0);
  851.         return;
  852.     }
  853.     beenThereDoneThat = true;
  854.     
  855.     ourDeviceDescriptor = *desc;
  856.     gSerialGlobals.deviceDescriptor = &ourDeviceDescriptor;
  857.     gSerialGlobals.deviceRef = device;
  858.     
  859.     InitializePB(&syncPB, device, syncCompletion);
  860.     InitializePB(&stallPB, device, stallHandler);
  861.     stallPB.usbStatus = kAvailableStatus;
  862.     InitializePB(&delayPB, device, delayHandler);
  863.     delayPB.usbStatus = kAvailableStatus;
  864.  
  865.     InitializePB(&gSerialGlobals.pb, device, ConfigurationHandler);
  866.     gSerialGlobals.pb.usbRefcon = 1;
  867.     gSerialGlobals.pb.usbBuffer = nil;
  868.  
  869.     gSerialGlobals.retries = 10;                // Let's keep it reasonable for now
  870.     
  871.     ConfigurationHandler(&gSerialGlobals.pb);
  872. }
  873.  
  874. /************************************************************************************/
  875. //
  876. //    InitializePB
  877. //
  878. //    Initial a parameter block.
  879. //
  880. /************************************************************************************/
  881.  
  882. static void InitializePB(USBPB *pb, USBDeviceRef ref, USBCompletion handler)
  883. {
  884.     pb->pbVersion = kUSBCurrentPBVersion;
  885.     pb->pbLength = sizeof(*pb);
  886.     pb->usbReference = ref;
  887.     pb->usbCompletion = handler;
  888.     pb->usbStatus = noErr;
  889.     
  890. }
  891.  
  892. static errCount, readActive = false;
  893.  
  894. /************************************************************************************/
  895. //
  896. //    readCompletion
  897. //
  898. //    Completion handler for USB reads.
  899. //
  900. /************************************************************************************/
  901.  
  902. void readCompletion(USBPB *pb)
  903. {
  904.  
  905.     TraceMessage(0,"\pEntering readCompletion");
  906.     
  907.     readActive = false;
  908.     
  909.     if (pb->usbStatus != kUSBAbortedError)    // are we being asked to quit?
  910.     {
  911.         if (pb->usbStatus == noErr){
  912.             LogData(kUSBIn, pb->usbActCount, pb->usbBuffer);
  913.             HAL_ShimInput(pb->usbBuffer, pb->usbActCount);
  914.         }else{
  915.             if ((pb->usbStatus != kUSBUnderRunErr) && errCount++ < 10)
  916.                 StatusMessage(gSerialGlobals.deviceRef, "\pRead completion error ", pb->usbStatus);
  917.                 
  918.             if (pb->usbStatus == kUSBEndpointStallErr)
  919.             {
  920. //                ClearDevice();
  921.             }
  922.             USBClearPipeStallByReference(gSerialGlobals.bulkIn);
  923.             if (pb->usbActCount > 0)
  924.                 HAL_ShimInput(pb->usbBuffer, pb->usbActCount);
  925.         }
  926.     
  927.         USBStartReadPolling();
  928.     }
  929. }
  930.  
  931. static UInt8    ioBuffer[64];
  932.  
  933. /************************************************************************************/
  934. //
  935. //    USBStartReadPolling
  936. //
  937. //    Start the USB read mechanism.
  938. //
  939. /************************************************************************************/
  940.  
  941. void USBStartReadPolling(void)
  942. {
  943.     OSStatus status;
  944.     
  945.     TraceMessage(0,"\pEntering USBStartReadPolling");
  946.     
  947.     if (gSerialGlobals.bulkIn && !readActive){
  948.         InitializePB(&readPB, gSerialGlobals.bulkIn, readCompletion);
  949.         readPB.usbBuffer = ioBuffer;
  950.         readPB.usbReqCount = sizeof(ioBuffer);
  951.         if(immediateError(status = USBBulkRead(&readPB)))
  952.         {
  953.             USBExpertFatalError(gSerialGlobals.deviceRef, status, "\pModemDriver: Couldn't start read polling", 0);
  954.             return;
  955.         }
  956.         readActive = true;
  957.     }
  958. }
  959.  
  960. /************************************************************************************/
  961. //
  962. //    USBStopReadPolling
  963. //
  964. //    You guessed it stop the USB read mechanism.
  965. //
  966. /************************************************************************************/
  967.  
  968. void USBStopReadPolling()
  969. {
  970.     StatusMessage(gSerialGlobals.deviceRef, "\pAborting Bulk-in pipe", 0);
  971.  
  972.     if (gSerialGlobals.bulkIn){
  973.         USBAbortPipeByReference(gSerialGlobals.bulkIn);
  974.     }
  975. }
  976.  
  977. static    UInt32    writeActive;
  978.  
  979. /************************************************************************************/
  980. //
  981. //    writeCompletion
  982. //
  983. //    USB write completion handler.
  984. //
  985. /************************************************************************************/
  986.  
  987. void writeCompletion(USBPB *pb)
  988. {
  989.     IOParam    *iopb;
  990.     
  991.     TraceMessage(0, "\pEntering writeCompletion");
  992.     
  993.     if (pb->usbStatus != kUSBAbortedError)    // are we being asked to quit?
  994.     {
  995.         if (pb->usbStatus != noErr) 
  996.         {
  997.             StatusMessage(gSerialGlobals.deviceRef, "\pWrite Failed", pb->usbStatus);
  998.             
  999.             if (pb->usbStatus == kUSBEndpointStallErr)
  1000.             {
  1001. //                ClearDevice();
  1002.             }
  1003.             USBClearPipeStallByReference(pb->usbReference);
  1004.         }
  1005.     }
  1006.     writeActive--;
  1007.     gGlobals->pbOut = nil;
  1008.     iopb = (IOParam *)pb->usbRefcon;
  1009.     iopb->ioActCount = pb->usbActCount;
  1010.     
  1011.     ShimIOComplete((union ParamBlockRec *)iopb, pb->usbStatus ? ioErr : 0);
  1012.  
  1013. }
  1014.  
  1015. /************************************************************************************/
  1016. //
  1017. //    USBSerialWrite
  1018. //
  1019. //    USB serial write routine.
  1020. //
  1021. /************************************************************************************/
  1022.  
  1023. OSStatus USBSerialWrite(IOParam *pb)
  1024. {
  1025.     OSStatus     status = noErr;
  1026.     
  1027.     TraceMessage(0, "\pEntering USBSerialWrite");
  1028.     
  1029.     if (writeActive){
  1030.         StatusMessage(gSerialGlobals.deviceRef, "\pOverlapping writes!", writePB.usbStatus);
  1031.         status = ioErr;
  1032.     } else if (gSerialGlobals.bulkOut){
  1033.         
  1034.         InitializePB(&writePB, gSerialGlobals.bulkOut, writeCompletion);
  1035.         writePB.usbRefcon = (UInt32)pb;
  1036.         writePB.usbBuffer = pb->ioBuffer;
  1037.         writePB.usbReqCount = pb->ioReqCount;
  1038.         
  1039.         LogData(kUSBOut, writePB.usbReqCount, writePB.usbBuffer);
  1040.         
  1041.         if(immediateError(status = USBBulkWrite(&writePB)))
  1042.         {
  1043.             StatusMessage(gSerialGlobals.deviceRef, "\pModemDriver: Couldn't start write", status);
  1044.         } else {
  1045.             writeActive++;
  1046.             status = 1;                        // pending
  1047.         }
  1048.     } else {
  1049.         status = ioErr;
  1050.     }
  1051.     return status;
  1052. }
  1053.  
  1054. /************************************************************************************/
  1055. //
  1056. //    KillUSBIO
  1057. //
  1058. //    Kill all USB io operations in progress.
  1059. //
  1060. /************************************************************************************/
  1061.  
  1062. void KillUSBIO(void)
  1063. {
  1064.     IOParam             *pb;
  1065.     ShimSerialGlobals     *globals = gGlobals;
  1066.     
  1067.     StatusMessage(gSerialGlobals.deviceRef, "\pModem: Killing all USB IO", 0);
  1068.  
  1069.     if (gSerialGlobals.bulkIn){
  1070.         USBAbortPipeByReference(gSerialGlobals.bulkIn);
  1071.         gSerialGlobals.bulkIn = nil;
  1072.     }
  1073.     if (gSerialGlobals.bulkOut){
  1074.         USBAbortPipeByReference(gSerialGlobals.bulkOut);
  1075.         gSerialGlobals.bulkOut = nil;
  1076.     }
  1077.     if (gSerialGlobals.interrupt){
  1078.         USBAbortPipeByReference(gSerialGlobals.interrupt);
  1079.         gSerialGlobals.interrupt = nil;
  1080.     }
  1081.     
  1082.     pb = (IOParam *)globals->pbIn;
  1083.     if (pb) {
  1084.         ShimIOComplete((union ParamBlockRec *)pb, ioErr);
  1085.         globals->pbIn = nil;
  1086.     }
  1087.     pb = (IOParam *)globals->pbOut;
  1088.     if (pb) {
  1089.         ShimIOComplete((union ParamBlockRec *)pb, ioErr);
  1090.         globals->pbOut = nil;
  1091.     }
  1092. }
  1093.  
  1094. /************************************************************************************/
  1095. //
  1096. //    InitLineCoding
  1097. //
  1098. //    Store the initial line settings.
  1099. //
  1100. /************************************************************************************/
  1101.  
  1102. void InitLineCoding(LineParms Line_Coding)
  1103. {
  1104.     serPB     *sp = &gSerialGlobals;
  1105.     
  1106.     TraceMessage(0, "\pEntering InitLineCoding");
  1107.         
  1108.     // save the settings
  1109.     sp->Line_Settings.DTERate1 = Line_Coding.DTERate1;
  1110.     sp->Line_Settings.DTERate2 = Line_Coding.DTERate2;
  1111.     sp->Line_Settings.CharFormat = Line_Coding.CharFormat;
  1112.     sp->Line_Settings.ParityType = Line_Coding.ParityType;
  1113.     sp->Line_Settings.DataBits = Line_Coding.DataBits;
  1114. }
  1115.  
  1116. /************************************************************************************/
  1117. //
  1118. //    DoDelay
  1119. //
  1120. //    Set up to do a USB delay.
  1121. //
  1122. /************************************************************************************/
  1123.  
  1124. void DoDelay(void)
  1125. {
  1126. serPB     *sp = &gSerialGlobals;
  1127.     
  1128.     TraceMessage(0, "\pEntering DoDelay");
  1129.     
  1130.     if (TimeoutDelayRequest())
  1131.         return;
  1132.             
  1133.     delayPB.usbStatus = noErr;
  1134.     delayPB.usbReference = sp->deviceRef;
  1135.     delayPB.usbRefcon = 1;
  1136.     delayPB.usbReqCount = 5;            // ~5ms (actually 5 frames at 1ms each)
  1137.     USBDelay(&delayPB);    
  1138. }
  1139.  
  1140. /************************************************************************************/
  1141. //
  1142. //    delayHandler
  1143. //
  1144. //    Completion handler for USB delay.
  1145. //
  1146. /************************************************************************************/
  1147.  
  1148. static void delayHandler(USBPB *pb)
  1149. {
  1150. serPB     *sp = &gSerialGlobals;
  1151.  
  1152.     TraceMessage(0, "\pEntering delayHandler");
  1153.  
  1154.     if (pb->usbStatus != noErr)
  1155.         StatusMessage(sp->deviceRef, "\pModemDriver: Delay fail?", pb->usbStatus);
  1156.  
  1157.     pb->usbStatus = kAvailableStatus;
  1158. }
  1159.  
  1160. /************************************************************************************/
  1161. //
  1162. //    ClearDevice
  1163. //
  1164. //    Set up and send ClearFeature request (for stall).
  1165. //
  1166. /************************************************************************************/
  1167.  
  1168. void ClearDevice(void)
  1169. {
  1170. serPB     *sp = &gSerialGlobals;
  1171.  
  1172.     TraceMessage(0, "\pEntering ClearDevice");
  1173.     
  1174.     if (TimeoutStallRequest())
  1175.         return;
  1176.         
  1177.     stallPB.usbStatus = noErr;
  1178.     stallPB.usbReference = sp->deviceRef;
  1179.     stallPB.usbRefcon = 1;
  1180.     stallHandler(&stallPB);
  1181. }
  1182.  
  1183. /************************************************************************************/
  1184. //
  1185. //    stallHandler
  1186. //
  1187. //    Completion handler for clear device/stall.
  1188. //
  1189. /************************************************************************************/
  1190.  
  1191. static void stallHandler(USBPB *pb)
  1192. {
  1193. serPB         *sp = &gSerialGlobals;
  1194. OSStatus    err = 0;
  1195.  
  1196.     TraceMessage(0, "\pEntering StallHandler");
  1197.     
  1198.     if (pb->usbStatus != noErr)
  1199.     {
  1200.         USBExpertFatalError(sp->deviceRef, pb->usbStatus, "\pModemDriver: Stall fail?", pb->usbRefcon);
  1201.         return;
  1202.     }
  1203.     
  1204.     // May have to do more here (like find out which endpoint is stalled) which is why an FSM is used
  1205.     // For the moment it's the Comm Class endpoint 0 which we'll try to clear (interupptComplete only for now)
  1206.     do{switch(pb->usbRefcon++)
  1207.     {
  1208.         case kEndpointStall:
  1209.             pb->usbStatus = noErr;
  1210.             pb->usbReference = sp->deviceRef;
  1211.             pb->pbVersion = kUSBCurrentPBVersion;    
  1212.             pb->usb.cntl.BMRequestType = USBMakeBMRequestType(kUSBOut, kUSBStandard, kUSBEndpoint);
  1213.     
  1214.             pb->usb.cntl.BRequest = kUSBRqClearFeature;
  1215.             pb->usb.cntl.WValue = 0;                        // Endpoint stall
  1216.             pb->usb.cntl.WIndex = 0;
  1217.             pb->usbReqCount = 0;
  1218.             pb->usbBuffer = nil;
  1219.  
  1220.             if(immediateError(err = USBDeviceRequest(pb)))
  1221.             {
  1222.                 StatusMessage(sp->deviceRef, "\pModem driver clearing device stall error", err);
  1223.             }
  1224.         break;
  1225.         
  1226.         case kStallDone:
  1227.         // stall should now be cleared
  1228.             if (pb->usbStatus != noErr)
  1229.                 USBExpertFatalError(sp->deviceRef, pb->usbStatus, "\pModemDriver: stall handler err for bRequest", pb->usb.cntl.BRequest);
  1230.     
  1231.             pb->usbStatus = kAvailableStatus;
  1232.         break;
  1233.         
  1234.         default:
  1235.             StatusMessage(sp->deviceRef, "\pInternal Error unused case in stall handler", (pb->usbRefcon-1));
  1236.         break;
  1237.     }
  1238.     break;    /* only execute once, unless continue used */
  1239.     }while(1);    /* so case can be reentered with a continue */
  1240. }
  1241.  
  1242. #if ((DebugOn > 0) && (LogOn > 0))
  1243.  
  1244. /************************************************************************************/
  1245. //
  1246. //    Asciify
  1247. //
  1248. //    Convert to Ascii character
  1249. //
  1250. /************************************************************************************/
  1251.  
  1252. UInt8 Asciify(UInt8 i)
  1253. {
  1254.  
  1255.     i &= 0xF;
  1256.     if ( i < 10 )
  1257.          return( '0' + i );
  1258.     else return( 55  + i );
  1259.     
  1260. }
  1261.  
  1262. #define dumplen        16        // Set this to the number of bytes to dump and the rest should work out correct
  1263.  
  1264. #define buflen        ((dumplen*2)+dumplen)+3
  1265. #define Asciistart    (dumplen*2)+3
  1266.  
  1267. /************************************************************************************/
  1268. //
  1269. //    USBLogData
  1270. //
  1271. //    Dumps the requested amount of data to the USB Expert log.
  1272. //
  1273. /************************************************************************************/
  1274.  
  1275. void USBLogData(UInt8 Dir, UInt32 Count, UInt8 *buf)
  1276. {
  1277.     UInt8    wlen, i, Aspnt, Hxpnt;
  1278.     UInt8    wchr;
  1279.     UInt8    LocBuf[buflen];
  1280.  
  1281.     for ( i=1; i<=buflen; i++)
  1282.     {
  1283.         LocBuf[i] = 0x20;
  1284.     }
  1285.     
  1286.     if (Dir == kUSBIn)
  1287.     {
  1288.         TraceMessage(1, "\pRead Complete");
  1289.     } else {
  1290.         TraceMessage(1, "\pWrite");
  1291.     }
  1292.  
  1293.     if (Count > dumplen)
  1294.     {
  1295.         wlen = dumplen;
  1296.     } else {
  1297.         wlen = Count;
  1298.     }
  1299.     
  1300.     if (wlen > 0)
  1301.     {
  1302.         Aspnt = Asciistart;
  1303.         Hxpnt = 1;
  1304.         for (i=1; i<=wlen; i++)
  1305.         {
  1306.             wchr = buf[i-1];
  1307.             LocBuf[Hxpnt++] = Asciify(wchr >> 4);
  1308.             LocBuf[Hxpnt++] = Asciify(wchr);
  1309.             if ((wchr < 0x20) || (wchr == 0x7F))         // Non printable characters
  1310.             {
  1311.                 LocBuf[Aspnt++] = 0x2E;                    // Replace with a period
  1312.             } else {
  1313.                 LocBuf[Aspnt++] = wchr;
  1314.             }
  1315.         }
  1316.         LocBuf[0] = (wlen + Asciistart) + 1;
  1317.         TraceMessage(1, LocBuf);
  1318.     } else {
  1319.         TraceMessage(1, "\pNo data - Actual count=0");
  1320.     }
  1321. }
  1322. #endif